#if !defined(LINES_BUFFER_H)
#define LINES_BUFFER_H

#include <string>
#include <vector>
#include <iostream>
	//std::cerr

#include "tbb/concurrent_queue.h"
    // tbb::concurrent_queue
#include "tbb/cache_aligned_allocator.h"
    // tbb::cache_aligned_allocator
#include "tbb/pipeline.h"
    // tbb::flow_control

#include <boost/shared_ptr.hpp>
    // boost::shared_ptr;


#include "recyclable_buffers.h"

typedef std::vector<char> 													t_storage; 
typedef t_storage::iterator                                             	i_storage; 
typedef std::vector<i_storage> 												ti_storage; 

// 
//  Alternative using intel cache_aligned_allocator
//  	Turned out to be slower...
//  	Need to revisit
// 
//typedef std::vector<char, tbb::cache_aligned_allocator<char> > 			t_storage; 
//typedef t_storage::iterator                                             	i_storage; 
//typedef std::vector<i_storage, tbb::cache_aligned_allocator<i_storage> > 	ti_storage; 




//########################################################################################

//	 t_lines_buffer_init
// 
//  		Holds parameters shared among a set of t_lines_buffers
//  		Could have used std::tuple<unsigned, unsigned> but a struct has defaults
//  			

//########################################################################################
struct t_lines_buffer_init
{
    std::size_t buffer_storage_cnt_lines;
    std::size_t buffer_line_length_hint;
    t_lines_buffer_init(std::size_t buffer_storage_cnt_lines_       = 50000U,
                        std::size_t buffer_line_length_hint_    	= 400U):
        buffer_storage_cnt_lines(buffer_storage_cnt_lines_),
        buffer_line_length_hint	(buffer_line_length_hint_) {}
};
    

//########################################################################################

//	 t_lines_buffer
// 
//  		Buffer that holds block of characters representing lines

//########################################################################################
class t_lines_buffer 
{
public:


    t_lines_buffer (const t_lines_buffer_init& init) : 
		line_count_offset(0), buffer_storage_cnt_lines(init.buffer_storage_cnt_lines), full(false)
	{
		storage.reserve(buffer_storage_cnt_lines * init.buffer_line_length_hint);
		line_ends.reserve(buffer_storage_cnt_lines + 1);
	}


	t_storage       					storage;
	ti_storage	     					line_ends;
	unsigned        					line_count_offset;

	void clear()
	{
		storage.clear();
		line_ends.clear();
		full = false;
	}

	// mostly for error checking
	bool is_full() const {return full;}
	template <typename ITER_TYPE>
	bool add_line( ITER_TYPE beg, ITER_TYPE end)
	{
		if (line_ends.size() < buffer_storage_cnt_lines)
		{
			storage.insert(storage.end(), beg, end);
			line_ends.push_back(storage.end());
			return true;
		}

		full = true;
		return false;
	}

    // report errors from this point
	void set_line_count_offset(unsigned line_count_offset_){line_count_offset = line_count_offset_;}

	// debug
	unsigned report_size() const
	{
		return storage.capacity() + line_ends.capacity() * 4;
	}
private:
	std::size_t     buffer_storage_cnt_lines;
	bool            full;
};



//########################################################################################

//	 t_lines_buffer_processor

//########################################################################################
class t_lines_buffer_processor
{
	//
	//  This is shared state in the sense that the data is not local to the thread running
	//  t_lines_buffer_processor.operator().
	// 
	//  At the same time, because the stream is being read serially, there is no need for
	//  synchronisation.
	// 
    struct t_lines_buffer_processor_shared_state
    {
        unsigned                        cnt_lines;
        std::string                     unprocessed_line;
        t_lines_buffer_processor_shared_state(unsigned cnt_lines_):
                            cnt_lines(cnt_lines_)
		{
		}
    };

    typedef boost::shared_ptr<t_lines_buffer_processor_shared_state>       sp_shared_state;
    sp_shared_state shared_state;
	std::istream&                   input_stream;
public:
	// copy c'tor
	t_lines_buffer_processor(const t_lines_buffer_processor& f)
		:shared_state(f.shared_state), input_stream(f.input_stream){}
	t_lines_buffer_processor(unsigned cnt_lines, std::istream& input_stream_)
		: shared_state(new t_lines_buffer_processor_shared_state(cnt_lines)), input_stream(input_stream_){}
	/*override*/ 
	t_lines_buffer * operator()(tbb::flow_control& fc) const;
};


#endif	// LINES_BUFFER_H


